package com.lqjava.flowable.beans.convertor.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lqjava.flowable.beans.utils.LocalDateTimeUtils;
import com.lqjava.flowable.beans.utils.RequestingThread;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

/**
 * 基础转换类
 * 主要功能: insert默认值,update默认值的赋值操作
 * @author lq 2021年3月21日 18:35:33
 */
public abstract class CommonConvertor {


    @Data
    @AllArgsConstructor
    public static class ProrValMethod{

        public LocalDateTime now(){
            return LocalDateTime.now();
        }
        public LocalDate date(){
            return LocalDate.now();
        }
        public LocalTime time(){
            return LocalTime.now();
        }
        public LocalDateTime dateTime(){
            return now();
        }
        public Long timestamp(){
            return new Date().getTime();
        }
        public String username(){
            return getRequestUser().getName();
        }
        public String userSk(){
            RequestingThread.RequestUser user = getRequestUser();
            return getRequestUser().getSk();
        }
        public Long userid(){
            RequestingThread.RequestUser user = getRequestUser();
            return getRequestUser().getId();
        }
        private RequestingThread.RequestUser getRequestUser(){
            return RequestingThread.REQUEST_USER.get() != null ? RequestingThread.REQUEST_USER.get() : new RequestingThread.RequestUser(null,null,null);
        }

    }
    @Data
    @AllArgsConstructor
    public static class ProrVal{

        private static final Map<String,Object> expression_map = new HashMap<>();
        private static final Map<String, Method> expression_method_map = new HashMap<>();

        static {

            Arrays.asList("now","date","time","dateTime","timestamp","username","userSk","userid").forEach(mn -> {
                try {
                    Method method = ProrValMethod.class.getMethod(mn);
                    if(method != null) expression_method_map.put(String.format("${%s}",mn),method);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            });
            expression_map.put("${yes}", 1);
            expression_map.put("${no}", 0);
        }



        private String prop;
        private Object value;

        public Object getValue(Method writeMethod) {

            if(value != null && value.toString().indexOf("${") > -1){
                String key = value.toString();
                Object val = expression_map.get(key) == null ? expression_method_map.get(key) : expression_map.get(key);
                if(val != null && val instanceof Method){
                    Method method = (Method)val;
                    try {
                        val = method.invoke(new ProrValMethod());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }

                value = val;
            }
            if(value != null && writeMethod != null && writeMethod.getParameterTypes().length == 1){
                value = ValueTypeConvert(value,writeMethod.getParameterTypes()[0]);
            }
            return value;
        }

        private Object ValueTypeConvert(Object val, Class<?> clazz) {

            if(val == null) return val;
            if(clazz.isAssignableFrom(val.getClass())) return val;

            String string = val.toString();

            System.out.println("ValueTypeConvert:" + clazz + " " + val);
            //String
            if(clazz.isAssignableFrom(String.class)) return string;
            //Long
            if(clazz.isAssignableFrom(Long.class))  return Long.valueOf(string);
            if(clazz.isAssignableFrom(long.class)) return Long.valueOf(string).longValue();
            //Integer
            if(clazz.isAssignableFrom(Integer.class)) return Integer.valueOf(string);
            if(clazz.isAssignableFrom(int.class)) return Integer.valueOf(string).intValue();
            //Double
            if(clazz.isAssignableFrom(Double.class)) return Double.valueOf(string);
            if(clazz.isAssignableFrom(double.class)) return Double.valueOf(string).doubleValue();
            //Float
            if(clazz.isAssignableFrom(Float.class)) return Float.valueOf(string);
            if(clazz.isAssignableFrom(float.class)) return Float.valueOf(string).floatValue();
            //Short
            if(clazz.isAssignableFrom(Short.class)) return Short.valueOf(string);
            if(clazz.isAssignableFrom(short.class)) return Short.valueOf(string).shortValue();
            //Boolean
            if(clazz.isAssignableFrom(Boolean.class)) return Boolean.valueOf(string);
            if(clazz.isAssignableFrom(boolean.class)) return Boolean.valueOf(string).booleanValue();
            //BigDecimal
            if(clazz.isAssignableFrom(BigDecimal.class)) return new BigDecimal(string);

            //LocalDate,DateTime,time
            if(clazz.isAssignableFrom(LocalTime.class)) return LocalDateTimeUtils.string2LocalTime(string);
            if(clazz.isAssignableFrom(LocalDate.class)) return LocalDateTimeUtils.string2LocalDate(string);
            if(clazz.isAssignableFrom(LocalDateTime.class)) return LocalDateTimeUtils.string2LocalDateTime(string);

            return val;
        }
    }

    public abstract List<ProrVal> getProrValList(boolean isInsert);

    public void initDefValue(Object obj){
        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(obj));
        initDefValue(obj,jsonObject.get("id") == null);
    }

    public void initDefValue(Object obj,boolean isInsert){
        List<ProrVal> prorValList = getProrValList(isInsert);
        initDefValue(obj,prorValList);
    }

    private void initDefValue(Object obj,List<ProrVal> prorValList){

        Class<? extends Object> clazz = obj.getClass();
        for (ProrVal pv : prorValList) {
            //获取字段的名称
            String fieldName = pv.getProp();
            // 内省机制获取属性信息
            PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, fieldName);
            if (pd == null) continue;
            // 获取当前字段的javabean读方法
            Method writeMethod = pd.getWriteMethod();
            if (writeMethod == null) continue;

            Object value = pv.getValue(writeMethod);
            if(value == null) continue;

            //属性赋值默认值
            try {
                writeMethod.invoke(obj, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
